/**
 * Copyright (c) Huawei Technologies Co., Ltd. 2024-2024. All rights reserved.
 */

#include <openssl/asn1.h>
#include <openssl/safestack.h>
#include <openssl/x509v3.h>
#include <openssl/pkcs12.h>
#include <openssl/objects.h>
#include <openssl/evp.h>
#include <stdbool.h>
#include <string.h>

struct RawX509Cert {
    const uint8_t* content;
    size_t size;
};

struct RawX509CertArray {
    struct RawX509Cert* buffer;
    size_t size;
};

struct RawPkey {
    const uint8_t* content;
    size_t size;
};

struct RawPKCS12 {
    const uint8_t* content;
    size_t size;
};

typedef struct DynMsg {
    bool found;
    char* errMsg;
} DynMsg;

DynMsg* mallocDynMsg(void)
{
    DynMsg* dynMsg = (DynMsg*)malloc(sizeof(DynMsg));
    if (dynMsg == NULL) {
        return NULL;
    }
    dynMsg -> found = true;
    dynMsg -> errMsg = NULL;
    return dynMsg;
}

void freeDynMsg(DynMsg* dynMsgPtr)
{
    if (dynMsgPtr == NULL) {
        return;
    }
    free((void*)dynMsgPtr);
}

extern void Dyn_CJParsePKCS12(const unsigned char* derBlob, size_t derLen, const char* pass, struct RawPkey* rawPkey,
    struct RawX509Cert* rawCert, struct RawX509CertArray* rawCa, DynMsg* dynMsg)
{
    PKCS12* p12 = d2i_PKCS12(NULL, &derBlob, derLen);
    if (p12 == NULL) {
        dynMsg -> found = false;
        dynMsg -> errMsg = "Failed to convert to p12.";
        return;
    }

    STACK_OF(X509)* ca = NULL;
    ca = (STACK_OF(X509)*)OPENSSL_sk_new_null();
    X509* cert = NULL;
    EVP_PKEY* pkey = NULL;

    int ret = 0;
    ret = PKCS12_parse(p12, pass, &pkey, &cert, &ca);
    if (ret == 0) {
        dynMsg -> found = false;
        dynMsg -> errMsg = "Failed to parse p12.";
        PKCS12_free(p12);
        OPENSSL_sk_free((void*)ca);
        return;
    }

    if (cert != NULL) {
        unsigned char* ppout = NULL;
        rawCert -> size = (size_t)i2d_X509(cert, &ppout);
        rawCert -> content = ppout;
    }

    if (pkey != NULL) {
        unsigned char* pkout = NULL;
        PKCS8_PRIV_KEY_INFO *p8 = EVP_PKEY2PKCS8(pkey);
        int ret = 0;
        if (p8 != NULL) {
            ret = i2d_PKCS8_PRIV_KEY_INFO(p8, &pkout);
            PKCS8_PRIV_KEY_INFO_free(p8);
        }
        rawPkey -> size = ret;
        rawPkey -> content = pkout;
    }

    int caLen = OPENSSL_sk_num((void*)ca);

    rawCa -> buffer = (struct RawX509Cert*)malloc(sizeof(struct RawX509Cert) * caLen);
    if (rawCa -> buffer == NULL) {
        PKCS12_free(p12);
        OPENSSL_sk_free((void*)ca);
        return;
    }

    X509* tempCert = NULL;
    for (int i = 0; i < caLen; i++) {
        tempCert = (X509*) OPENSSL_sk_value((void*)ca, i);
        unsigned char* tmpOut = NULL;
        rawCa -> buffer[i].size = i2d_X509(tempCert, &tmpOut);
        rawCa -> buffer[i].content = tmpOut;
    }
    rawCa -> size = caLen;

    PKCS12_free(p12);
    OPENSSL_sk_free((void*)ca);
}

extern void* Dyn_CJBuildPKCS12(const char* pass, const char* name, struct RawPkey* rawPkey, struct RawX509Cert* rawCert,
             struct RawX509CertArray* rawCa, int nid_key, int nid_cert, int iter, int mac_iter,
             DynMsg* dynMsg)
{
    EVP_PKEY* pkey = d2i_AutoPrivateKey(NULL, &rawPkey -> content, (long)rawPkey -> size);

    X509* cert = d2i_X509(NULL, &rawCert -> content, (long)rawCert -> size);

    STACK_OF(X509)* ca = (STACK_OF(X509)*)OPENSSL_sk_new_null();

    for (int i = 0; i < (int)rawCa -> size; i++) {
        X509* certTmp = NULL;
        certTmp = d2i_X509(NULL, &rawCa -> buffer[i].content, rawCa -> buffer[i].size);
        OPENSSL_sk_push((void*)ca, certTmp);
    }

    PKCS12* p12 = NULL;

    int keyType = 0;
    p12 = PKCS12_create(pass, name, pkey, cert, ca, nid_key, nid_cert, iter, mac_iter, keyType);
    X509_free(cert);
    OPENSSL_sk_free((void*)ca);
    EVP_PKEY_free(pkey);
    if(p12 == NULL) {
        dynMsg -> found = false;
        dynMsg -> errMsg = "Failed to create p12.";
        return NULL;
    }

    unsigned char* p12Der = NULL;
    int p12Len = i2d_PKCS12(p12, &p12Der);
    PKCS12_free(p12);
    struct RawPKCS12* rawP12 = (struct RawPKCS12*) malloc(sizeof(struct RawPKCS12));
    rawP12 -> content = p12Der;
    rawP12 -> size = p12Len;
    return rawP12;
}